﻿using System.Text;
using System.Text.Json;

namespace Bread.Utility;

class JsonNode
{
    public string Name { get; set; }

    public int Depth { get; set; }

    /// <summary>
    /// json path 中指定的数组元素位置
    /// </summary>
    public int Index { get; set; } = -1;

    /// <summary>
    /// json对象的实际位置
    /// </summary>
    public int Offset { get; set; } = -1;

    /// <summary>
    /// 当前遍历到的位置
    /// </summary>
    public int CurrentOffset { get; set; } = 0;


    public bool IsArray => Index >= 0 || Offset >= 0;

    public JsonNode(string name, int depth, int index = -1)
    {
        Name = name;
        Index = index;
        Depth = depth;
    }
}

file enum ReadType
{
    None,
    String,
    Number,
    Boolean,
}

public static class JsonReader
{
    static Queue<JsonNode> GetJPath(string json, string jsonPath, Utf8JsonReader reader)
    {
        if (jsonPath.Length == 0)
            throw new ArgumentException("invalid json path");

        var pathes = jsonPath.TrimStart('$').Split('.', StringSplitOptions.RemoveEmptyEntries);
        if (pathes.Length == 0)
            throw new ArgumentException("invalid json path");

        // 1.0 parse json path to path nodes queue
        int depth = 1; // path depth
        var nodes = new Queue<JsonNode>();

        foreach (var path in pathes) {
            var splits = path.Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
            if (splits.Length == 1) {
                nodes.Enqueue(new(splits[0].Trim(), depth));
            }
            else if (splits.Length == 2) {
                if (int.TryParse(splits[1], out var v)) {
                    nodes.Enqueue(new(splits[0].Trim(), depth, v));
                    depth++; // 数组级数加1
                }
            }
            depth++;
        }

        // 2.0 convert json-path index to json data index
        int index = 0;
        var offset = -1;
        foreach (var node in nodes) {

            if (offset != -1) {
                node.Offset = offset;
                offset = -1;
            }

            if (node.Index >= 0) {
                offset = node.Index;
                if (index != nodes.Count - 1) {
                    node.Index = -1;
                }
            }

            index++;
        }

        return nodes;
    }

    /// Wait for C# 13
    //static T ParseJson<T>(Utf8JsonReader parser, Queue<JsonNode> nodes, JsonTokenType type, Func<Utf8JsonReader, T> func)
    //{
    //    // 3.0 parse json
    //    bool matched = false;
    //    JsonNode target = nodes.Dequeue();

    //    while (parser.Read()) {
    //        switch (parser.TokenType) {
    //            case JsonTokenType.StartObject:
    //                break;
    //            case JsonTokenType.StartArray:
    //                break;

    //            case JsonTokenType.PropertyName:
    //                var name = parser.GetString();
    //                if (target.Name == name) {
    //                    if (target.Depth == parser.CurrentDepth) {
    //                        if (target.Offset >= 0) {
    //                            if (target.CurrentOffset != target.Offset) {
    //                                target.CurrentOffset++;
    //                                break;
    //                            }
    //                            else {
    //                                target.CurrentOffset = 0;
    //                            }
    //                        }

    //                        if (nodes.Count > 0) {
    //                            target = nodes.Dequeue();
    //                        }
    //                        else {
    //                            matched = true;
    //                        }
    //                    }
    //                }
    //                break;
    //            case JsonTokenType.String:
    //                if (matched) {
    //                    if (target.Index >= 0) {
    //                        if (target.Index == target.CurrentOffset) {
    //                            return func(parser);
    //                        }
    //                        target.CurrentOffset++;
    //                    }
    //                    else {
    //                        return func(parser);
    //                    }
    //                }
    //                break;
    //            case JsonTokenType.Number:
    //            case JsonTokenType.True:
    //            case JsonTokenType.False:
    //                if (matched) {
    //                    throw new InvalidDataException("call other function for valid data type");
    //                }
    //                break;
    //        }
    //    }

    //    throw new InvalidDataException("data not found");
    //}

    /// <summary>
    /// 使用 JsonPath 读取 json 文本中特定的项<br/>
    /// 仅支持 . 和 [] 操作符
    /// </summary>
    /// <param name="json">json 文本</param>
    /// <param name="jsonpath">json-path</param>
    /// <returns></returns>
    public static string ReadString(string json, string jsonPath)
    {
        var bytes = Encoding.UTF8.GetBytes(json).AsSpan();
        var parser = new Utf8JsonReader(bytes);
        var nodes = GetJPath(json, jsonPath, parser);

       // 3.0 parse json
        bool matched = false;
        JsonNode target = nodes.Dequeue();

        while (parser.Read()) {
            switch (parser.TokenType) {
                case JsonTokenType.StartObject:
                    break;
                case JsonTokenType.StartArray:
                    break;

                case JsonTokenType.PropertyName:
                    var name = parser.GetString();
                    if (target.Name == name) {
                        if (target.Depth == parser.CurrentDepth) {
                            if (target.Offset >= 0) {
                                if (target.CurrentOffset != target.Offset) {
                                    target.CurrentOffset++;
                                    break;
                                }
                                else {
                                    target.CurrentOffset = 0;
                                }
                            }

                            if (nodes.Count > 0) {
                                target = nodes.Dequeue();
                            }
                            else {
                                matched = true;
                            }
                        }
                    }
                    break;
                case JsonTokenType.String:
                    if (matched) {
                        if (target.Index >= 0) {
                            if (target.Index == target.CurrentOffset) {
                                return parser.GetString() ?? string.Empty;
                            }
                            target.CurrentOffset++;
                        }
                        else {
                            return parser.GetString() ?? string.Empty;
                        }
                    }
                    break;
                case JsonTokenType.Number:
                case JsonTokenType.True:
                case JsonTokenType.False:
                    if (matched) {
                        throw new InvalidDataException("call other function for valid data type");
                    }
                    break;
            }
        }

        throw new InvalidDataException("data not found");
    }

    /// <summary>
    /// 使用 JsonPath 读取 json 文本中特定的项<br/>
    /// 仅支持 . 和 [] 操作符
    /// </summary>
    /// <param name="json">json 文本</param>
    /// <param name="jsonpath">json-path</param>
    /// <returns></returns>
    public static long ReadLong(string json, string jsonPath)
    {
        var bytes = Encoding.UTF8.GetBytes(json).AsSpan();
        var parser = new Utf8JsonReader(bytes);

        var nodes = GetJPath(json, jsonPath, parser);

        // 3.0 parse json
        bool matched = false;
        JsonNode target = nodes.Dequeue();

        while (parser.Read()) {
            switch (parser.TokenType) {
                case JsonTokenType.StartObject:
                    break;
                case JsonTokenType.StartArray:
                    break;

                case JsonTokenType.PropertyName:
                    var name = parser.GetString();
                    if (target.Name == name) {
                        if (target.Depth == parser.CurrentDepth) {
                            if (target.Offset >= 0) {
                                if (target.CurrentOffset != target.Offset) {
                                    target.CurrentOffset++;
                                    break;
                                }
                                else {
                                    target.CurrentOffset = 0;
                                }
                            }

                            if (nodes.Count > 0) {
                                target = nodes.Dequeue();
                            }
                            else {
                                matched = true;
                            }
                        }
                    }
                    break;

                case JsonTokenType.Number:
                    if (matched) {
                        if (target.Index >= 0) {
                            if (target.Index == target.CurrentOffset) {
                                return parser.GetInt64();
                            }
                            target.CurrentOffset++;
                        }
                        else {
                            return parser.GetInt64();
                        }
                    }
                    break;
                case JsonTokenType.String:
                case JsonTokenType.True:
                case JsonTokenType.False:
                    if (matched) {
                        throw new InvalidDataException("call other function for valid data type");
                    }
                    break;
            }
        }

        throw new InvalidDataException("data not found");
    }

    /// <summary>
    /// 使用 JsonPath 读取 json 文本中特定的项<br/>
    /// 仅支持 . 和 [] 操作符
    /// </summary>
    /// <param name="json">json 文本</param>
    /// <param name="jsonpath">json-path</param>
    /// <returns></returns>
    public static double ReadDouble(string json, string jsonPath)
    {
        var bytes = Encoding.UTF8.GetBytes(json).AsSpan();
        var parser = new Utf8JsonReader(bytes);

        var nodes = GetJPath(json, jsonPath, parser);

        // 3.0 parse json
        bool matched = false;
        JsonNode target = nodes.Dequeue();

        while (parser.Read()) {
            switch (parser.TokenType) {
                case JsonTokenType.StartObject:
                    break;
                case JsonTokenType.StartArray:
                    break;

                case JsonTokenType.PropertyName:
                    var name = parser.GetString();
                    if (target.Name == name) {
                        if (target.Depth == parser.CurrentDepth) {
                            if (target.Offset >= 0) {
                                if (target.CurrentOffset != target.Offset) {
                                    target.CurrentOffset++;
                                    break;
                                }
                                else {
                                    target.CurrentOffset = 0;
                                }
                            }

                            if (nodes.Count > 0) {
                                target = nodes.Dequeue();
                            }
                            else {
                                matched = true;
                            }
                        }
                    }
                    break;

                case JsonTokenType.Number:
                    if (matched) {
                        if (target.Index >= 0) {
                            if (target.Index == target.CurrentOffset) {
                                return parser.GetDouble();
                            }
                            target.CurrentOffset++;
                        }
                        else {
                            return parser.GetDouble();
                        }
                    }
                    break;
                case JsonTokenType.String:
                case JsonTokenType.True:
                case JsonTokenType.False:
                    if (matched) {
                        throw new InvalidDataException("call other function for valid data type");
                    }
                    break;
            }
        }

        throw new InvalidDataException("data not found");
    }


    /// <summary>
    /// 使用 JsonPath 读取 json 文本中特定的项<br/>
    /// 仅支持 . 和 [] 操作符
    /// </summary>
    /// <param name="json">json 文本</param>
    /// <param name="jsonpath">json-path</param>
    /// <returns></returns>
    public static bool ReadBool(string json, string jsonPath)
    {
        var bytes = Encoding.UTF8.GetBytes(json).AsSpan();
        var parser = new Utf8JsonReader(bytes);

        var nodes = GetJPath(json, jsonPath, parser);

        // 3.0 parse json
        bool matched = false;
        JsonNode target = nodes.Dequeue();

        while (parser.Read()) {
            switch (parser.TokenType) {
                case JsonTokenType.StartObject:
                    break;
                case JsonTokenType.StartArray:
                    break;

                case JsonTokenType.PropertyName:
                    var name = parser.GetString();
                    if (target.Name == name) {
                        if (target.Depth == parser.CurrentDepth) {
                            if (target.Offset >= 0) {
                                if (target.CurrentOffset != target.Offset) {
                                    target.CurrentOffset++;
                                    break;
                                }
                                else {
                                    target.CurrentOffset = 0;
                                }
                            }

                            if (nodes.Count > 0) {
                                target = nodes.Dequeue();
                            }
                            else {
                                matched = true;
                            }
                        }
                    }
                    break;

                case JsonTokenType.True:
                case JsonTokenType.False:
                    if (matched) {
                        if (target.Index >= 0) {
                            if (target.Index == target.CurrentOffset) {
                                return parser.GetBoolean();
                            }
                            target.CurrentOffset++;
                        }
                        else {
                            return parser.GetBoolean();
                        }
                    }
                    break;
                case JsonTokenType.String:
                case JsonTokenType.Number:
                    if (matched) {
                        throw new InvalidDataException("call other function for valid data type");
                    }
                    break;
            }
        }

        throw new InvalidDataException("data not found");
    }
}
